/*
 * preProcessor.c
 *
 *  Created on: May 5, 2014
 *      Author: Hercules
 */

#include "preProcessor.h"

void generatePreProcessedFile(FILE *program_file, char *file_name) {
	TokenList* token_list;
	FILE *processed_file = fopen(file_name, "w");

	token_list = getTokenListWithEquSolved(program_file);
	token_list = solveIfSymbols(token_list);
	token_list = formatSections(token_list);
	TokenList *token_node_to_write = token_list;

	//escreve toda a lista de tokens no arquivo de saida
	while (token_node_to_write != NULL ) {
		fprintf(processed_file, "%s", token_node_to_write->token->token_string);
		if (token_node_to_write->token->token_delimiter != '\0') {
			fprintf(processed_file, "%c",
					token_node_to_write->token->token_delimiter);
		}

		token_node_to_write = token_node_to_write->next;
	}
	freeTokenList(token_list);
	fclose(processed_file);
}
TokenList* formatSections(TokenList* token_list) {
	bool is_data_first = false;
	TokenList *token_node = token_list;
	//Verifica se o sessao de data ou texto aparece primeiro na lista de tokens
	while (token_node != NULL ) {
		if (strcmp(token_node->token->token_string, "DATA") == 0) {
			is_data_first = true;
			break;
		} else if (strcmp(token_node->token->token_string, "TEXT") == 0) {
			is_data_first = false;
			break;
		}
		token_node = token_node->next;
	}
	//inverte a ordem caso a sessao de data esteja antes
	if (is_data_first) {
		TokenList* data_section_begin = token_node->last;
		token_node = token_node->next;
		while (token_node != NULL
				&& strcmp(token_node->token->token_string, "TEXT") != 0) {
			token_node = token_node->next;
		}
		if (token_node == NULL ) {
			printf("ERRO : Section text ausente!");
			exit(1);
		}

		TokenList* temp = token_node->last;
		while (token_node->next != NULL ) {
			token_node = token_node->next;
		}
		token_node->next = data_section_begin;
		temp->last->next = NULL;
		temp->last->token->token_delimiter = '\0';
		token_node->token->token_delimiter = '\n';
		return temp;
	} else {
		return token_list;
	}
}
void freeTokenList(TokenList* token_list) {
	while (token_list != NULL ) {
		TokenList* temp = token_list;
		token_list = token_list->next;
		free(temp);
	}
}
TokenList* solveIfSymbols(TokenList* token_list) {
	TokenList* token_node_to_cmp = token_list;

	//enquanto ainda existirem tokens a serem comparados
	while (token_node_to_cmp != NULL ) {
		//se o token for um if
		if (strcmp(token_node_to_cmp->token->token_string, "IF") == 0) {
			TokenList* ifArgument = token_node_to_cmp->next;
			if (ifArgument != NULL ) {
				//se o argumento do if for 1
				// liga o token anterior a diretiva if ao token posterior a diretiva
				if (strcmp(ifArgument->token->token_string, "1") == 0) {
					token_node_to_cmp->last->next = ifArgument->next;
					ifArgument->next->last = token_node_to_cmp->last;
					free(token_node_to_cmp);
					token_node_to_cmp = ifArgument->next;
					free(ifArgument);
				}
				//se o argumento for zero ignora tambem a linha apos o if
				else {
					TokenList *last_false_if_token = ifArgument->next;
					//enquanto nao chegar ao final da proxima linha
					while (last_false_if_token->token->token_delimiter != '\n'
							&& last_false_if_token->token->token_delimiter
									!= '\0') {
						TokenList* toFree = last_false_if_token;
						last_false_if_token = last_false_if_token->next;
						free(toFree);
					}
					token_node_to_cmp->last->next = last_false_if_token->next;
					//caso o final da linha seja nao seja o ultimo token da lista
					if (last_false_if_token->next != NULL ) {
						last_false_if_token->next->last =
								token_node_to_cmp->last;
					}
					free(token_node_to_cmp);
					token_node_to_cmp = last_false_if_token->next;
					free(last_false_if_token);
				}
			}
		}
		if (token_node_to_cmp != NULL ) {
			token_node_to_cmp = token_node_to_cmp->next;
		}
	}
	return token_list;
}
TokenList* getTokenListWithEquSolved(FILE* program_file) {
	TokenList* token_list;
	EquList* equ_list;
	token_list = (TokenList*) malloc(sizeof(TokenList));
	equ_list = (EquList*) malloc(sizeof(EquList));
	getTokenAndEquList(program_file, token_list, equ_list);
	token_list = solveEquSymbols(token_list, equ_list);

	return token_list;
}

TokenList* solveEquSymbols(TokenList* token_list, EquList *equ_list_head) {
	TokenList *token_list_node = token_list;
	EquList *equ_list_node;
	//enquanto a lista de tokens nao for vazia
	while (token_list_node != NULL ) {
		equ_list_node = equ_list_head->next;
		//enquanto a lista de equ simbolos nao for vazia
		while (equ_list_node != NULL ) {
			//caso encontre um token com o simbolo da lista de equ simbolos
			// troca o valor do token pelo valor do equ
			if (strcmp(equ_list_node->symbol,
					token_list_node->token->token_string) == 0) {

				strcpy(token_list_node->token->token_string,
						equ_list_node->value);
			}
			equ_list_node = equ_list_node->next;
		}

		token_list_node = token_list_node->next;
	}

	equ_list_node = equ_list_head;
	//libera lista de equ
	while (equ_list_node != NULL ) {
		EquList* temp = equ_list_node;
		equ_list_node = equ_list_node->next;
		free(temp);
	}

	return token_list;

}
void getTokenAndEquList(FILE* program_file, TokenList *token_list,
		EquList *equ_list) {

	token_list->token = NULL;
	token_list->next = NULL;
	equ_list->next = NULL;

	EquList* last_equ_node = equ_list;
	Token* last_read_token = NULL;
	Token* read_token = NULL;
	TokenList *last_token_node = NULL;

	//enquanto existirem tokens a serem lidos
	while ((read_token = getNextToken(program_file, last_read_token)) != NULL ) { //Enquanto existirem tokens a serem lidos
		//caso o token lido seja uma diretiva EQU
		// adiciona o valor do equ a lista de diretivas equ
		if (strcmp(read_token->token_string, "EQU") == 0) { // Caso a diretiva EQU seja lida, adiciona um novo elemento na lista de sinonimos
			EquList *new_equ_node = (EquList*) malloc(sizeof(EquList));
			strcpy(new_equ_node->symbol, last_read_token->token_string);
			read_token = getNextToken(program_file, NULL );
			strcpy(new_equ_node->value, read_token->token_string);

			last_equ_node->next = new_equ_node;
			new_equ_node->next = NULL;
			last_equ_node = new_equ_node;

			//retira o label do equ da lista de tokens
			TokenList * toFree = last_token_node;
			last_token_node = last_token_node->last;
			last_token_node->next = NULL;
			last_read_token = last_token_node->token;
			free(toFree);

		} else { //Caso o token nao seja um EQU apenas adiciona na lista de tokens

			if (last_token_node == NULL ) { //caso seja o primeiro elemento da lista
				token_list->next = NULL;
				token_list->last = NULL;
				token_list->token = read_token;
				last_token_node = token_list;
			} else { //caso contrario
				TokenList *new_token_node = (TokenList*) malloc(
						sizeof(TokenList));
				new_token_node->next = NULL;
				new_token_node->last = last_token_node;
				new_token_node->token = read_token;
				last_token_node->next = new_token_node;
				last_token_node = new_token_node;
			}
			last_read_token = read_token;
		}
	}

}
